#ifndef NANE_SUBSYSTEM_H
#   define NANE_SUBSYSTEM_H

#   include "nane/core/crt/ModuleManager.h"
#   include "nane/core/assert.h"
#   include "nane/core/log.h"

#   include <cstring>

namespace nane 
{

    //
    // Wraps ISubSystem interface implementation
    //
    template<class I>
    class SubSystem
    {
    protected:
        class Implementation
        {
        public:
            Implementation( const char* _interfaceName )
                : interfaceName( _interfaceName )
                , interface( NULL )
                , interfaceModule( NullPtr )
            {
                NANE_ASSERT( interfaceName != NULL, "Interface name must be valid" );
            }

            ~Implementation()
            {

            }

        public:
            const char* interfaceName;
            I* interface;
            ModulePtr interfaceModule;
        };

    public:
        //
        // Consturctor
        // -
        // params:
        //   _interfaceName - interface name of subsytem, must be valid compile time string
        //
        SubSystem( Implementation* _impl )
            : m( _impl )
        {
            NANE_ASSERT( m != NULL, "Implementation must be valid" );
        }
        
        // 
        // Destructor
        // -
        // SubSystem must be shut down and implementation released before this call
        //
        ~SubSystem()
        {
            //NANE_ASSERT( m == NULL, "Implementation must be NULL (SubSystem must be shut down)" );
        }
        
    public:
        //
        // initializes SubSystem with first matching interface implemenation from ModuleManager
        // -
        // result:
        //   returns true if SubSystem was successfully initialized, false otherwise
        //   returns true if SubSystem already initialized
        //
        bool Initialize()
        {
            if( m->interface != NULL )   // already initialized
            {
                return true;
            }
            
            bool initialized = false;
            ModulePtr subSystemModule = ModuleManager::GetModuleByInterface(m->interfaceName);
            if( subSystemModule != NullPtr )
            {
                initialized = Initialize(subSystemModule);
            }
            return initialized;            
        }
        
        //
        // initializes SubSystem with Module containing interface implementation
        // -
        // params:
        //   _interfaceModule - non-null ModulePtr containing interface implementation
        // result:
        //   returns true if SubSystem was successfully initialized or already initalized with same module
        //   returns false if SubSystem failed to initilaize or was initialized by different module
        //
        bool Initialize( const ModulePtr& _interfaceModule )
        {
            NANE_ASSERT_RV( _interfaceModule != NullPtr, "Module must be valid", false );
            NANE_ASSERT_RV( std::strcmp(_interfaceModule->GetInfo()->interfaceName, m->interfaceName) == 0, "Module must implement corresponding interface", false );
            if( m->interfaceModule == _interfaceModule )
            {
                return true;
            }
            else if( m->interfaceModule != NullPtr )
            {
                LOG_ERROR("%1 SubSystem already initialized with different module. You must shutdown it first") << m->interfaceName;
                return false;
            }

            IBase* baseInterface = NULL;
            _interfaceModule->GetInfo()->Create(&baseInterface);
            if( baseInterface != NULL )
            {
                if( initialize(static_cast<I*>(baseInterface)) != false )
                {
                    m->interfaceModule = _interfaceModule;
                }
                else
                {
                    _interfaceModule->GetInfo()->Release(baseInterface);
                }
            }
            return m->interfaceModule != NullPtr;
        }
        
        // 
        // initializes SubSystem with interface implementation
        // -
        // params:
        //   _interface - non-null valid interface implementation pointer
        // result:
        //   returns true if SubSystem was successfully initialized or already initialized with same interface
        //   returns false if SubSystem failed to initialize or was already initialized by different interface
        //
        bool initialize( I* _interface )
        {
            NANE_ASSERT_RV( _interface != NULL, "Interface must be valid", false );
            if( m->interface == _interface )
            {
                return true;
            }
            else if( m->interface != NULL )
            {
                LOG_ERROR("%1 SubSystem already initialized with different interface. You must shutdown it first") 
                        << m->interfaceName;
                return false;
            }
            if( _interface->Initialize() == true )
            {
                m->interface = _interface;
            }

            return m->interface != NULL;
        }
        
        // 
        // shutdowns SubSystem
        // -
        // results:
        //   SubSystem is shut down.
        //   valid noop if SubSystem wasn't initialized
        //
        void Shutdown()
        {
            if( m->interface != NULL )
            {
                m->interface->Shutdown();
                if( m->interfaceModule != NullPtr )
                {
                    m->interfaceModule->GetInfo()->Release(m->interface);
                    m->interfaceModule = NullPtr;
                }
                m->interface = NULL;
            }
        }
        
    protected:
        Implementation* m;
    };

}   // namespace nane

#endif	// NANE_SUBSYSTEM_H
